package com.jacky.path_provider_ex;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.media.MediaMetadataRetriever;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.os.StatFs;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.DisplayMetrics;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.jacky.MediaMetadataRetrieverProxy;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.util.PathUtils;

/**
 * PathProviderExPlugin
 */
public class PathProviderExPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener,
        PluginRegistry.RequestPermissionsResultListener {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  private MethodChannel channel;
  private Context context;
  private Activity activity;
  private ActivityPluginBinding pluginBinding;
  private FilePermissionHandler filePermissionHandler;

  @Override
  public void onAttachedToEngine(FlutterPluginBinding flutterPluginBinding) {
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "com.jacky/path_provider");
    channel.setMethodCallHandler(this);
    this.context = flutterPluginBinding.getApplicationContext();
  }

  @Override
  public void onAttachedToActivity(ActivityPluginBinding binding) {
    this.pluginBinding = binding;
    activity = binding.getActivity();
    binding.addActivityResultListener(this);
    binding.addRequestPermissionsResultListener(this);

  }

  @Override
  public void onDetachedFromActivity() {
    if (pluginBinding != null) {
      this.pluginBinding.removeActivityResultListener(this);
      this.pluginBinding.removeRequestPermissionsResultListener(this);
    }
    pluginBinding = null;
  }

  @Override
  public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
    if (filePermissionHandler == null) {
      return false;
    }
    return filePermissionHandler.onActivityResult(requestCode, resultCode, data);
  }

  @Override
  public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (filePermissionHandler == null) {
      return false;
    }
    return filePermissionHandler.onRequestPermissionsResult(requestCode, permissions, grantResults);
  }

  @Override
  public void onDetachedFromActivityForConfigChanges() {}

  @Override
  public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
    binding.removeActivityResultListener(this);
    activity = null;
  }

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    switch (call.method) {
      case "getTemporaryDirectory" :
        result.success(context.getCacheDir().getPath());
        break;
      case "getApplicationDocumentsDirectory" : 
        result.success(PathUtils.getDataDirectory(context));
        break;
      case "getStorageDirectory" : {
        File dir = this.context.getExternalFilesDir(null);
        result.success(dir == null ? null : dir.getAbsolutePath());
        break;
      }
      case "getExternalCacheDirectories":
        result.success(getPathProviderExternalCacheDirectories());
        break;
      case "getExternalStorageDirectories": {
        final Integer type = call.argument("type");
        final String directoryName = androidType(type);
        List<String> list = new ArrayList<>();
        list.add(Environment.getExternalStoragePublicDirectory(directoryName).toString());
        result.success(list);
        break;
      }
      case "getApplicationSupportDirectory":
        result.success(PathUtils.getFilesDir(context));
        break;
      case "getExternalSdcardPath": {
        final FilePermissionHandler filePermissionHandler = getFilePermissionHandler();
        if (filePermissionHandler == null) {
          result.error("-1", "activity尚未初始化", null);
        } else {
          activity.runOnUiThread(() -> filePermissionHandler.request(new FilePermissionHandler.OnPermissionCallback() {
            @Override
            public void onSuccess() {
              result.success(Environment.getExternalStorageDirectory().getAbsolutePath());
            }

            @Override
            public void onFail(String code, String message) {
              result.error(code, message, null);
            }
          }));
        }
        break;
      }
      case "getVideoDuration":
        checkVideoDuration(call.argument("path"), result);
        break;
      case "getAvailableSize":
        result.success(getStorageSize());
        break;
      case "getAvailableStoragePercent":
        result.success(getAvailableStoragePercent());
        break;
      case "getDownloadsDirectory":
        result.success(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());
        break;
      case "getLibraryDirectory":
        String database = call.argument("name");
        result.success(context.getDatabasePath(database).getAbsolutePath());
        break;
      case "installApp": {
        boolean r = installApk(call.arguments.toString());
        if (r) {
          result.success(true);
        } else {
          result.error("-1", "activity尚未初始化", null);
        }
        break;
      }
      case "environmentCheck" : {
        result.success(new SecureCheck().checkPhoneSecure(activity));
      }break;
      case "openAdbUI" :
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
        break;
      case "nativeImageCachePath" : {
        String name = call.argument("name");
        String type = call.argument("type");
        boolean useCache = call.argument("useCache");
        File file = new File(context.getFilesDir(), "cache_" + name);
        if(useCache && file.exists()) {
          result.success(file.getAbsolutePath());
          return;
        }
        Resources res = context.getResources();
        int id = res.getIdentifier(name, type, context.getPackageName());
        if(id == 0) result.error("-10004", "resource not found: " + name, null);
        else {
          Bitmap bm = BitmapFactory.decodeResource(res, id);
          if(saveBitmap(Bitmap.CompressFormat.PNG, bm,100, file)) {
            result.success(file.getAbsolutePath());
          } else {
            result.error("-10004", "bitmap保存失败", null);
          }
        }
      }break;
      case "exifInfo":
        Map map = getExifInfoByFile(context, call.arguments.toString());
        result.success(map);
        break;
      case "thumbnailImage":{
        String path = call.argument("path");
        String thumbPath = call.argument("thumbPath");
        int width = call.argument("width");
        int height = call.argument("height");
        result.success(generateImageThumb(path, thumbPath, width, height));
      }break;
      case "mediaScanner":{
        MediaScannerConnection.scanFile(context, new String[]{call.arguments.toString()}, null, null);
        break;
      }
      default: result.notImplemented();
    }
  }

  public boolean installApk(String path) {
    final FilePermissionHandler f = getFilePermissionHandler();
    if (f == null) {
      return false;
    } else {
      f.installApp(path);
      return true;
    }
  }

  private void checkVideoDuration(String videoPath, Result result) {
//    if(TextUtils.isEmpty(videoPath)) {
//      result.error("-10001", "Video path is null", null);
//      return;
//    }
//    if(!new File(videoPath).exists()) {
//      result.error("-10002", "Video path not found", null);
//      return;
//    }
    try {
      result.success(getVideoDuration(videoPath));
    } catch (Exception e) {
      result.error("-10003", e.getMessage(), e);
    }
  }

  /**
   * 返回视频文件的播放时长
   * @param videoPath  文件路径
   */
  public static int getVideoDuration(String videoPath) {
    if(TextUtils.isEmpty(videoPath)) {
//      result.error("-10001", "Video path is null", null);
      Utils.Log("VideoDuration: Video path is null", null);
      return 0;
    }
    if(!new File(videoPath).exists()) {
//      result.error("-10002", "Video path not found", null);
      Utils.Log("VideoDuration: Video path not found", null);
      return 0;
    }

    int d = 0;
    try {
      MediaMetadataRetrieverProxy mmr = new MediaMetadataRetrieverProxy();
      mmr.setDataSource(videoPath);
      String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); // 播放时长单位为毫秒
      d = Integer.parseInt(duration);
      mmr.release();
    } catch (Exception e) {
      Utils.Log("VideoDuration", e);
    }
    return d;
  }

  private List<String> getPathProviderExternalCacheDirectories() {
    List<String> paths = new ArrayList<>();
    File[] var2 = this.context.getExternalCacheDirs();
    for (File dir : var2) {
      if (dir != null) {
        paths.add(dir.getAbsolutePath());
      }
    }
    return paths;
  }

  private List<String> getPathProviderExternalStorageDirectories(String type) {
    List<String> paths = new ArrayList<>();
    File[] var3 = this.context.getExternalFilesDirs(type);
    for (File dir : var3) {
      if (dir != null) {
        paths.add(dir.getAbsolutePath());
      }
    }
    return paths;
  }

  String androidType(Integer dartIndex) throws IllegalArgumentException {
    if (dartIndex == null) {
      return null;
    }

    switch (dartIndex) {
      case 0: return Environment.DIRECTORY_MUSIC;
      case 1: return Environment.DIRECTORY_PODCASTS;
      case 2: return Environment.DIRECTORY_RINGTONES;
      case 3: return Environment.DIRECTORY_ALARMS;
      case 4: return Environment.DIRECTORY_NOTIFICATIONS;
      case 5: return Environment.DIRECTORY_PICTURES;
      case 6: return Environment.DIRECTORY_MOVIES;
      case 7: return Environment.DIRECTORY_DOWNLOADS;
      case 8: return Environment.DIRECTORY_DCIM;
      case 9: return Environment.DIRECTORY_DOCUMENTS;
      default: throw new IllegalArgumentException("Unknown index: " + dartIndex);
    }
  }

  /**
   * 获取存储空间大小
   * @return 返回数组，数组[0]值代表总大小，数组[1]值代表可获取空间大小
   */
  private long getStorageSize() {
    if(!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
      return 0;
    }
    //android 29即Q后
    File file = Environment.getExternalStorageDirectory();
    StatFs statFs = new StatFs(file.getPath());
    //获取总block数量
//    long blockCount = statFs.getBlockCountLong();
    //每个block的size
    long blockSize = statFs.getBlockSizeLong();
    //获取剩余可使用的block数量
    long blockAvailable = statFs.getAvailableBlocksLong();
//    return new long[]{blockSize * blockCount, blockSize * blockAvailable};
    return blockSize * blockAvailable;
  }

  /**
   * 获取剩余可用空间占总空间的百分比( 0 ~ 100)
   */
  public static int getAvailableStoragePercent() {
    if(!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
      return 0;
    }
    //android 29即Q后
    File file = Environment.getExternalStorageDirectory();
    StatFs statFs = new StatFs(file.getPath());
    //获取总block数量
    long blockCount = statFs.getBlockCountLong();
    //获取剩余可使用的block数量
    long blockAvailable = statFs.getAvailableBlocksLong();
    return (int) (blockAvailable / blockCount * 100);
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
    context = null;
  }

  @Nullable
  private FilePermissionHandler getFilePermissionHandler() {
    if (pluginBinding == null) {
      return null;
    }
    if (filePermissionHandler != null) {
      return filePermissionHandler;
    }
    final Activity activity = pluginBinding.getActivity();
    if (filePermissionHandler == null) {
      filePermissionHandler = new FilePermissionHandler(activity);
    }
    return filePermissionHandler;
  }

  @SuppressLint("WrongThread")
  private static boolean saveBitmap(Bitmap.CompressFormat format, Bitmap bm, int quality, File file) {
    if(bm == null) return  false;

    file.getParentFile().mkdirs();

    FileOutputStream fos = null;
    try {
      fos = new FileOutputStream(file);
      bm.compress(format, quality,fos);
      return true;
    } catch (Throwable e) {
      Utils.Log("saveBitmap", e);
//      result.error("-10004", "bitmap保存失败", e);
    } finally {
      if(fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
        }
      }
      bm.recycle();
    }
    return  false;
  }

  public static Map<String, Object> getExifInfoByFile(Context context, String path){
    Map<String, Object> map = new HashMap<>();
    try {
      String suffix = path.substring(path.lastIndexOf('.') + 1);
      if(Utils.isVideo(suffix)) {
        MediaMetadataRetrieverProxy retriever = new MediaMetadataRetrieverProxy();
        retriever.setDataSource(path);
        String width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
        String height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
        String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
        String date = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
        String location = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
        retriever.release();

        double[] loc = Utils.transferVideoGps(location);
        map.put("width", width);
        map.put("height", height);
        map.put("duration", duration);
        map.put("time", Utils.formatMediaDate(date));
        map.put("latitude", loc[0]);
        map.put("longitude", loc[1]);
      } else {
        ExifInterface exifInterface = new ExifInterface(path);
        String latitude = exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
        String longitude = exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
        String datetime = exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
        int width = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
        int height = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
        boolean isHorizontal = orientation == ExifInterface.ORIENTATION_NORMAL || orientation == ExifInterface.ORIENTATION_ROTATE_180;
        //2023:12:04 11:28:06  将第一个和第二个冒号 替换为 短横
        if(!TextUtils.isEmpty(datetime)) {
          datetime = datetime.replaceFirst(":", "-");
          datetime = datetime.replaceFirst(":", "-");
        }
        map.put("latitude", Utils.formatGps(latitude));
        map.put("longitude", Utils.formatGps(longitude));
        map.put("time", datetime);
        //图片如果有旋转角度，则宽高互换
        map.put("width", isHorizontal ? width : height);
        map.put("height", isHorizontal ? height : width);
      }
      return map;
    } catch (Exception e) {
      Utils.Log("getExifInfoByFile", e);
    }
    return null;
  }

  /**
   * 生成图片的缩略图
   * @param path  图片原地址
   * @param thumbPath 缩略图保存地址
   * @param width  缩略图宽的最大值
   * @param height 缩略图高的最大值
   * @return
   */
  public boolean generateImageThumb(String path, String thumbPath, int width, int height) {
    // 图片解析的配置
    BitmapFactory.Options opts = new BitmapFactory.Options();
    // 设置为True的意思：不是真的去解析 图片，只是获取图片的头部信息，比如宽，高等
    opts.inJustDecodeBounds = true;
    // 重要步骤：使BitmapFactory考虑Exif信息进行自动旋转
    opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
    opts.inTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
    opts.inScreenDensity = Resources.getSystem().getDisplayMetrics().densityDpi;
    BitmapFactory.decodeFile(path, opts);
    // 获取图片的真实的高度和宽度
    int imageWidth = opts.outWidth;
    int imageHeight = opts.outHeight;
    // 计算缩放比例
    int scaleX = imageWidth / width;
    int scaleY = imageHeight / height;

    int scale = 1;
    if (scaleX > scaleY & scaleY >= 1) {
      scale = scaleX;
    }
    if (scaleY > scaleX & scaleX >= 1) {
      scale = scaleY;
    }
    // 真的要去解析图片了，所以设置为false
    opts.inJustDecodeBounds = false;
    // 采样率 设置参数，设置缩放比
    opts.inSampleSize = scale;
    Bitmap bitmap = BitmapFactory.decodeFile(path, opts);

    int degree = readPictureDegree(path);
    if(degree > 0) {
      bitmap = rotaingImageView(degree, bitmap);
    }

    File file = new File(thumbPath);
    return saveBitmap(Bitmap.CompressFormat.JPEG, bitmap, 60, file);
  }

  /**
   * 读取图片属性：旋转的角度
   *
   * @param path
   *            图片绝对路径
   * @return degree旋转的角度
   */
  private int readPictureDegree(String path) {
    int degree = 0;
    try {
      ExifInterface exifInterface = new ExifInterface(path);
      int orientation = exifInterface.getAttributeInt(
              ExifInterface.TAG_ORIENTATION,
              ExifInterface.ORIENTATION_NORMAL);
      switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
          degree = 90;
          break;
        case ExifInterface.ORIENTATION_ROTATE_180:
          degree = 180;
          break;
        case ExifInterface.ORIENTATION_ROTATE_270:
          degree = 270;
          break;
      }
    } catch (IOException e) {
      Utils.Log("readPictureDegree", e);
    }
    return degree;
  }
  /**
   * 旋转图片
   *
   * @param degree
   * @param bitmap
   * @return Bitmap
   */
  private Bitmap rotaingImageView(int degree, Bitmap bitmap) {
    // 旋转图片 动作
    Matrix matrix = new Matrix();
    ;
    matrix.postRotate(degree);
    // 创建新的图片
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
            bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    return resizedBitmap;
  }
}
